package com.yumeng.common.utils;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.yumeng.common.data.constants.DateConstants;
import com.yumeng.common.data.enums2.JsonScene;
import com.yumeng.common.exception.YumengException;
import com.yumeng.common.helper.AssertHelper;
import com.yumeng.common.helper.LocalDateHelper;
import com.yumeng.common.runtime.GlobalVariableHelper;
import com.yumeng.common.test.bean.TestItem;
import jakarta.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Supplier;

/**
 * json工具类
 *
 * JsonNode如果用path，key不存在是会返回com.fasterxml.jackson.databind.node.MissingNode
 * @author wxd
 * @date 2021/7/16 9:31
 */
@Slf4j
public class JacksonUtils {

    private static final ObjectMapper mapper;

    /**
     * 设置一些通用的属性
     */
    static {
        mapper = new ObjectMapper();
        initObjectMapper(mapper, JsonScene.UTILS);
    }

    /**
     * 初始化jackson
     * TODO 后续根据使用场景不断完善
     *
     * @param objectMapper
     * @param scene
     */
    public static void initObjectMapper(ObjectMapper objectMapper, JsonScene scene){
        TimeZone defTZ = TimeZone.getDefault();
        log.info("JSON框架初始化【{}】,时区:{},ObjectMapper:{}", scene, defTZ.getID(), objectMapper);
        GlobalVariableHelper.getInstance().registerTimeZone(defTZ.getID());
        SimpleModule module = new SimpleModule();

        //======日期设置 格式、时区
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(LocalDateHelper.fmt));
        module.addSerializer(LocalDate.class, new LocalDateSerializer(LocalDateHelper.dateFmt));
        module.addSerializer(LocalTime.class, new LocalTimeSerializer(LocalDateHelper.timeFmt));
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(LocalDateHelper.fmt));
        module.addDeserializer(LocalDate.class, new LocalDateDeserializer(LocalDateHelper.dateFmt));
        module.addDeserializer(LocalTime.class, new LocalTimeDeserializer(LocalDateHelper.timeFmt));
        objectMapper.setDateFormat(new SimpleDateFormat(DateConstants.YYYY_MM_DD_HH_MM_SS));
        objectMapper.setTimeZone(defTZ);
        objectMapper.setLocale(Locale.CHINA);
        //前端获取Long型数值精度丢失
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(long.class, ToStringSerializer.instance);














        //======提供LocalDateTime类型支持  提示：Java 8 date/time type `java.time.LocalDateTime` not supported by default
        objectMapper.registerModule(new JavaTimeModule());

        objectMapper.registerModule(module);

    }


    private void initBak(ObjectMapper objectMapper){

/*          #jackson改为在java中配置
    #default-property-inclusion: ALWAYS*/

        // 通过该方法对mapper对象进行设置，所有序列化的对象都将按改规则进行系列化
        // Include.Include.ALWAYS 默认
        // Include.NON_DEFAULT 属性为默认值不序列化
        // Include.NON_EMPTY 属性为 空（""） 或者为 NULL 都不序列化，则返回的json是没有这个字段的。这样对移动端会更省流量
        // Include.NON_NULL 属性为NULL 不序列化
        //objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); //会覆盖yml中的配置
        //objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 允许出现特殊字符和转义符
        //objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        // 允许出现单引号
        //objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

        //objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        // 字段保留，将null值转为""







        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() {
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializer) throws IOException {
                gen.writeString("");
            }
        });


        // 如果存在过滤标记的属性，则忽略不报错
        //objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, true);
        // 如果存在未知属性，则忽略不报错 缺少get set
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 允许key没有双引号
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        // 允许key有单引号
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        // 允许整数以0开头
        //objectMapper.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);
        // 允许字符串中存在回车换行控制符
        //objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        //键按自然顺序输出
        //objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
        //如果为null，则隐藏字段
        //objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        //忽略值为默认值的属性
        //objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);

        //objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        //null 返回 ""

        //放开注释 CacheManager中的log 会报错
        //注释后，不知道缓存会不会有问题 默认的检测机制：public修饰的字段->public修饰的getter/的setter
        //objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        //objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);



        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer(){
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializer) throws IOException {
                gen.writeString("");
            }
        });


/*        builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ObjectMapper objectMapper = builder.createXmlMapper(false)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
                .timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                .build();*/


        // null数据返回
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 反序列化时候遇到不匹配的属性并不抛出异常
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 序列化时候遇到空对象不抛出异常
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 反序列化的时候如果是无效子类型,不抛出异常
        objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
        // 不使用默认的dateTime进行序列化,
        objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
        // 数据精度问题
        SimpleModule simpleModule = new SimpleModule()
                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(BigDecimal.class, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        // 配置Java 8时间日期模块
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule());


        //=====================redis
        // 指定要序列化的域，field,get和set,以及修饰符范围，ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

/*        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);*/

        // 指定序列化输入的类型，类必须是非final修饰的，final修饰的类，比如String,Integer等会跑出异常
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);

/*        Jackson2JsonRedisSerializer redisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        redisSerializer.setObjectMapper(objectMapper);*/
    }



    //region toJsonString

    public static String toJsonString(Object obj) {
        return obj != null ? toJsonString(obj, () -> "", false) : "";
    }

    public static String toFormatJSONString(Object obj) {
        return obj != null ? toJsonString(obj, () -> "", true) : "";
    }

    public static String toJsonString(Object obj, Supplier<String> defaultSupplier, boolean format) {
        try {
            if (obj == null) {
                return defaultSupplier.get();
            }
            if (obj instanceof JsonNode) {
                return obj.toString();
            }
            if (obj instanceof String) {
                return obj.toString();
            }
            if (obj instanceof Number) {
                return obj.toString();
            }
            if (format) {
                return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            }
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("Jackson转换字符串（toJsonString）过程失败", e);
        }
        return defaultSupplier.get();
    }

    //endregion

    //region toJavaObject

    public static Object toJavaObject(String value) {
        return StringUtils.isNotBlank(value) ? toJavaObject(value, Object.class, () -> null) : null;
    }
    public static <T> T toJavaObject(String value, Class<T> tClass) {
        return StringUtils.isNotBlank(value) ? toJavaObject(value, tClass, () -> null) : null;
    }

    public static <T> T toJavaObject(Object obj, Class<T> tClass) {
        return obj != null ? toJavaObject(toJsonString(obj), tClass, () -> null) : null;
    }

    public static <T> T toJavaObject(String value, Class<T> tClass, Supplier<T> defaultSupplier) {
        try {
            if (StringUtils.isBlank(value)) {
                return defaultSupplier.get();
            }
            if (String.class.equals(tClass)){
                return (T)value;
            }
            return mapper.readValue(value, tClass);
        } catch (JsonProcessingException e) {
            log.error(String.format("Jackson转换对象（toJavaObject）过程失败:\n %s\n %s", value, tClass), e);
        }
        return defaultSupplier.get();
    }

    //endregion

    //region toList

    public static <T> List<T> toList(String value, Class<T> tClass) {
        return StringUtils.isNotBlank(value) ? toList(value, tClass, () -> null) : null;
    }

    public static <T> List<T> toList(Object obj, Class<T> tClass) {
        return obj != null ? toList(toJsonString(obj), tClass, () -> null) : null;
    }

    public static <T> List<T> toList(String value, Class<T> tClass, Supplier<List<T>> defaultSupplier) {
        try {
            if (StringUtils.isBlank(value)) {
                return defaultSupplier.get();
            }
            //获取泛型的Collection Type 效果类似TypeReference<T> typeReference
            JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, tClass);
            return mapper.readValue(value, javaType);
        } catch (Throwable e) {
            log.error(String.format("Jackson转换对象列表（toList）过程失败:\n%s\n%s", value, tClass), e);
        }
        return defaultSupplier.get();
    }

    //endregion

    //region Map

    public static <T> Map<String, T> toMap(String value, Class<T> tClass){
        return StringUtils.isNotBlank(value) ? toMap(value, tClass, () -> null) : null;
    }

    public static <T> Map<String, T> toMap(Object obj, Class<T> tClass){
        return obj != null ? toMap(toJsonString(obj), tClass, () -> null) : null;
    }

    public static <T> Map<String, T> toMap(String value, Class<T> tClass, Supplier<Map<String, T>> defaultSupplier){
        try {
            if (StringUtils.isBlank(value)) {
                return defaultSupplier.get();
            }
            //获取泛型的Collection Type 效果类似TypeReference<T> typeReference
            JavaType javaType = mapper.getTypeFactory().constructParametricType(Map.class, String.class, tClass);
            return mapper.readValue(value, javaType);
        } catch (Throwable e) {
            log.error(String.format("Jackson转换对象列表（toMap）过程失败:\n%s\n%s", value, tClass), e);
        }
        return defaultSupplier.get();
    }

    //endregion

    //region JsonNode

    public static JsonNode parseJsonNode(@Nonnull Object obj){
        return mapper.valueToTree(obj);
    }
    public static <T> T parseObject(@Nonnull JsonNode node, Class<T> tClass){
        try {
            return mapper.treeToValue(node, tClass);
        } catch (JsonProcessingException e) {
            log.error(String.format("JacksonUtils（parseObject）过程失败:\nClass：%s", tClass.getName()), e);
        }
        return null;
    }
    public static JsonNode parseJsonNode(@Nonnull String jsonStr){
        if (StringUtils.isBlank(jsonStr)){
            return null;
        }
        try {
            return mapper.readTree(jsonStr);
        } catch (JsonProcessingException e) {
            log.error(String.format("JacksonUtils（parseJsonNode）过程失败:\njsonStr：%s", jsonStr), e);
        }
        return null;
    }

    //endregion

    //region get 参数为节点名称；节点不存在时，返回null；

    public static List<String> getTexts(String jsonStr, String[] keys){
        return getTexts(jsonStr, keys, true);
    }
    public static List<String> getTexts(@Nonnull final JsonNode node, String[] keys){
        return getTexts(node, keys, true);
    }

    public static List<String> getTexts(String jsonStr, String[] keys, boolean strict){
        AssertHelper.assertNotBlank(jsonStr, "JacksonUtils getTests jsonStr为空");
        //AssertHelper.assertNotNull(keys, "JacksonUtils getTests keys为空");
        JsonNode node = parseJsonNode(jsonStr);
        if (node == null){
            return null;
        }
        return getTexts(node, keys, strict);
    }

    public static List<String> getTexts(@Nonnull final JsonNode node, String[] keys, boolean strict){
        AssertHelper.assertNotNull(keys, "JacksonUtils getTests keys为空");
        List<String> list = new ArrayList<>();
        for (String key : keys){
            String val = getText(node, key);
            if (strict && val == null){
                throw new YumengException("JacksonUtils getTests [%s]不存在", key);
            }
            if (val != null){
                list.add(val);
            }
        }
        return list;
    }

    public static String getText(String jsonStr, String key){
        AssertHelper.assertNotBlank(jsonStr, "JacksonUtils getText jsonStr为空");
        //AssertHelper.assertNotBlank(key, "JacksonUtils getText key为空");
        JsonNode node = parseJsonNode(jsonStr);
        if (node != null){
            return getText(node, key);
        }
        return null;
    }
    public static String getText(@Nonnull final JsonNode node, String key){
        AssertHelper.assertNotBlank(key, "JacksonUtils getText2 key为空");
        JsonNode tempNode = node.get(key);
        if (tempNode != null){
            return tempNode.asText();
        }
        return null;
    }

    //endregion

    //region path 参数为节点名称；节点不存在时，返回MissingNode；

    public static String pathText(String jsonStr, String key){
        AssertHelper.assertNotBlank(jsonStr, "JacksonUtils pathText jsonStr为空");
        //AssertHelper.assertNotBlank(key, "JacksonUtils pathText key为空");
        JsonNode node = parseJsonNode(jsonStr);
        if (node != null){
            return pathText(node, key);
        }
        return null;
    }
    public static String pathText(@Nonnull final JsonNode node, String key){
        AssertHelper.assertNotBlank(key, "JacksonUtils pathText2 key为空");
        JsonNode tempNode = node.path(key);
        if (!tempNode.isMissingNode()){
            return tempNode.asText();
        }
        return null;
    }


    //endregion

    //region at 参数为节点路径；节点不存在（路径错误）时，返回MissingNode；

    public static String atText(String jsonStr, String key){
        AssertHelper.assertNotBlank(jsonStr, "JacksonUtils atText jsonStr为空");
        //AssertHelper.assertNotBlank(key, "JacksonUtils atText key为空");
        JsonNode node = parseJsonNode(jsonStr);
        if (node != null){
            return atText(node, key);
        }
        return null;
    }
    public static String atText(@Nonnull final JsonNode node, String key){
        AssertHelper.assertNotBlank(key, "JacksonUtils atText2 key为空");
        JsonNode tempNode = node.at(key);
        if (!tempNode.isMissingNode()){
            return tempNode.asText();
        }
        return null;
    }

    public static JsonNode at(@Nonnull final JsonNode node, String key){
        AssertHelper.assertNotBlank(key, "JacksonUtils at key为空");
        return node.at(key);
    }

    //endregion

    //region tools

    /** 通过json方式克隆对象 */
    public static <T> T jsonClone(Object obj, Class<T> tClass) {
        return obj != null ? toJavaObject(toJsonString(obj), tClass) : null;
    }

    public static boolean isJsonStr(String data){
        JsonNode node = parseJsonNode(data);
        if (node == null){
            return false;
        }
        return true;
    }

    //endregion





    public static void main(String[] args) throws IOException {

        /*TestUserAuthInfo-PROPERTY##:{"@class":"com.yumeng.core.test.bean.TestUserAuthInfo","userId":3129687177,"username":"username_434g","realName":"realName_KetO","password":"password_U322","salt":"salt_pv1d","isStoped":false}

        TestUserAuthInfo-WRAPPER_OBJECT##:{"com.yumeng.core.test.bean.TestUserAuthInfo":{"userId":6695946369,"username":"username_MiEO","realName":"realName_Q5e9","password":"password_nmDl","salt":"salt_15J5","isStoped":false}}

        TestUserAuthInfo-WRAPPER_ARRAY##:["com.yumeng.core.test.bean.TestUserAuthInfo",{"userId":8658527242,"username":"username_oPE9","realName":"realName_gA7k","password":"password_EipV","salt":"salt_r50E","isStoped":false}]

        TestUserAuthInfo-EXISTING_PROPERTY##:{"userId":6425484362,"username":"username_64t6","realName":"realName_wm9h","password":"password_1Y2h","salt":"salt_bq48","isStoped":false}*/

/*        TestUserAuthInfo authInfo = new TestUserAuthInfo(1);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        log.info("TestUserAuthInfo-WRAPPER_ARRAY##:{}", toJsonString(authInfo));*/

/*        String testStr = toJavaObject("你好", String.class);
        log.info("testStr##:{}", testStr);*/

        TestItem item = new TestItem("张三", "张三同学", 1);
        log.info("item##:{}", toJsonString(item));

        /*List<TestItem> list = new ArrayList<>();
        list.add(new TestItem("张三", "张三同学", 1));
        list.add(new TestItem("李四", "李四你好", 0));
        list.add(new TestItem("王二", "王二同事", 1));

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code1", "AAA");
        map.put("code2", true);
        map.put("item", new TestItem("小黄", "小黄你好啊",  2));
        map.put("itemList", list);

        TestItem item = new TestItem("张三", null, 1);
        log.info("jacksonItem##:{}", toJsonString(item));
        // >>>>> jackson
        // map对象转json
        String jackson = toJsonString(map);
        log.info("jackson##:{}", jackson);



        JsonNode node = mapper.readTree(jackson);
        JsonNode node33 = node.get("/item33List");//get=>null
        JsonNode node34 = node.path("/item34List");//path=>missingNode
        JsonNode node35 = node.at("/item35List");//at=>missingNode
        log.info("path:{}", node.path("code1").toString());
        log.info("get:{}", node.get("code2"));
        log.info("at33:{}", node.at("/item33List"));
        log.info("at:{}", node.at("/itemList/1/itemName"));
        JsonNode nodeArr = node.at("/itemList");
        nodeArr.forEach(jsonNode -> {
            try {
                TestItem item2 = toJavaObject(jsonNode, TestItem.class);
                log.info("item:{}", toJsonString(item2));
            } catch (Exception e) {
                e.printStackTrace();
            }

        });

        JsonNode node1 = node.findValue("item");
        JsonNode node2 = node.get("item");
        JsonNode node3 = node.path("item");
        log.info("find_textValue:{}", node.textValue());
        log.info("find_asText:{}", node.asText());
        log.info("find_toString:{}", node.toString());
        log.info("find_toString:" + node.toString());*/

    }
}
